/*----------------------------------------------------------------------------------------
*
*  Copyright 2019, Gao Hai Hui, <fromzeropoint@126.com>.  All rights reserved.
*  https://gitee.com/helloworldghh/xoskit.git
*  Use of this source code is governed by a MIT license
*  that can be found in the License file.
*
----------------------------------------------------------------------------------------*/
#include "../import/head.h"
#include "../data_struct/head.h"
#include "../macro/head.h"
#include "aio_thread.h"
#include "aio_helper.h"
#include "aio_impl.h"
#include "mgr.h"

/////////////////////////////////////////////////////////////////////////////////////////
// 
namespace xos_aio
{
    //////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    static aio_impl::ITEM_DATA xos_item_data;

    //////////////////////////////////////////////////////////////////////////////////////////////////////////

    aio_impl::aio_impl()
    {
        init_data();
    }

    aio_impl::~aio_impl()
    {
        term();
    }

    ////////////////////////////////////////////////////////////////////////////////////
    // 

    aio_impl::lpGetAcceptExSockaddrs aio_impl::get_extrack_addr( SOCKET s )
    {
        DWORD dwBytes = 0, dwErr = 0;
        lpGetAcceptExSockaddrs f = 0;
        GUID guid = WSAID_GETACCEPTEXSOCKADDRS;

        dwErr = WSAIoctl( s,
            SIO_GET_EXTENSION_FUNCTION_POINTER,
            &guid,
            sizeof( guid ),
            &f,
            sizeof( f ),
            &dwBytes,
            NULL,
            NULL );

        if( (DWORD)SOCKET_ERROR == dwErr )
        {
            dwErr = WSAGetLastError();
            f = 0;
        }

        return f;
    }

    aio_impl::lpConnectEx aio_impl::get_connect_ex( SOCKET s )
    {
        DWORD dwBytes = 0, dwErr = 0;
        lpConnectEx f = 0;
        GUID guid = WSAID_CONNECTEX;

        dwErr = WSAIoctl( s,
            SIO_GET_EXTENSION_FUNCTION_POINTER,
            &guid,
            sizeof( guid ),
            &f,
            sizeof( f ),
            &dwBytes,
            NULL,
            NULL );

        if( (DWORD)SOCKET_ERROR == dwErr )
        {
            dwErr = WSAGetLastError();
            f = 0;
        }

        return f;
    }

    aio_impl::lpAcceptEx aio_impl::get_accept_ex( SOCKET s )
    {
        DWORD dwBytes = 0, dwErr = 0;
        lpAcceptEx f = 0;
        GUID guid = WSAID_ACCEPTEX;

        dwErr = WSAIoctl( s,
            SIO_GET_EXTENSION_FUNCTION_POINTER,
            &guid,
            sizeof( guid ),
            &f,
            sizeof( f ),
            &dwBytes,
            NULL,
            NULL );

        if( (DWORD)SOCKET_ERROR == dwErr )
        {
            dwErr = WSAGetLastError();
            f = 0;
        }

        return f;
    }

    ////////////////////////////////////////////////////////////////////////////////////
    // 

    int aio_impl::get_xos_item_data( ITEM_DATA *& item_ptr )
    {
        int ret = 0;
        xos_item_data.set_container( mgr::container() );
        item_ptr = &xos_item_data;
        return ret;
    }

    ////////////////////////////////////////////////////////////////////////////////////
    // 

    int aio_impl::init_data()
    {
        int ret = 0;
        m_lpGetAcceptExSockaddrs = 0;
        m_lpConnectEx = 0;
        m_lpAcceptEx = 0;
        m_pAioThread = 0;
        m_pCallback = 0;
        m_hIocp = 0;
        return ret;
    }

    ////////////////////////////////////////////////////////////////////////////////////
    // 

    int aio_impl::notify( xos_box::i_msg *& pMsg ) const
    {
        int nRet = 0;
        m_pCallback->notify( pMsg );
        return nRet;
    }

    int aio_impl::init()
    {
        int nRet = 0;

        if( 0 == nRet )
        {
            SOCKET s = ::WSASocketW( AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED );
            m_lpGetAcceptExSockaddrs = get_extrack_addr( s );
            m_lpConnectEx = get_connect_ex( s );
            m_lpAcceptEx = get_accept_ex( s );
            closesocket( s );
        }

        if( 0 == nRet )
        {
            m_hIocp = CreateIoCompletionPort( INVALID_HANDLE_VALUE, 0, 0, 1 );
            if( !m_hIocp )
            {
                nRet = 1;
            }
        }

        if( 0 == nRet )
        {
            m_pAioThread = new aio_thread( this );
            m_pAioThread->init();
        }

        return nRet;
    }

    int aio_impl::term()
    {
        int nRet = 0;

        if( m_pAioThread )
        {
            PostQueuedCompletionStatus( m_hIocp, 0, 0, 0 );
            m_pAioThread->term();
            delete m_pAioThread;
            m_pAioThread = 0;
        }

        if( m_hIocp )
        {
            CloseHandle( m_hIocp );
            m_hIocp = 0;
        }

        xos_stl::release_interface( m_pCallback );
        m_closing_list.put_back_to_pool();

        init_data();

        return nRet;
    }

    ////////////////////////////////////////////////////////////////////////////////////
    // 

    // 
    // aio request
    // 
    // m_nData[0] : enumAioType
    // 
    int aio_impl::post_request( xos_box::i_msg *& pMsg )
    {
        int nRet = 0;
        aio_helper obj( this );
        obj.post_msg( pMsg );
        return nRet;
    }

    // 
    // set callback. 
    // 
    int aio_impl::set_callback( xos::f_runnable pCallback )
    {
        int ret = 0;
        if( !m_pCallback )
        {
            m_pCallback = mgr::get()->notify()->clone();
        }
        m_pCallback->set_callback( pCallback );
        return ret;
    }
} // xos_aio
